/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package it.tukano.odt;

import it.tukano.fun.Function0;
import it.tukano.fun.Function1;
import it.tukano.io.FileExt;
import it.tukano.io.MemBuffer;
import it.tukano.io.Zipper;
import it.tukano.lang.StringExt;
import it.tukano.xml.XmlDoc;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *  helper used to fill forms from a meta property of an odt document
 * @author utente4
 */
public class OdtFiller {
    private final Collection<? extends OdtField> fields;
    private final List<Function1<Map<String, String>, ?>> metaGenerators = new LinkedList<Function1<Map<String, String>, ?>>();
    
    public OdtFiller(Collection<? extends OdtField> fields) {
        this.fields = fields;
    }
    
    public void addMetaGenerator(Function1<Map<String, String>, ?> fun) {
        metaGenerators.add(fun);
    }
    
    protected String getFormFieldValueHexBuffer() {
        String hex = null;
        try {
            MemBuffer buffer = MemBuffer.newInstance();
            Map<String, String> formFieldValues = getFormFieldValues();
            for (Function1<Map<String, String>, ?> function1 : metaGenerators) {
                function1.apply(formFieldValues);
            }
            ObjectOutputStream out = new ObjectOutputStream(buffer.getOutputStream());
            out.writeObject(formFieldValues);
            out.close();
            hex = buffer.getHexString();
        } catch(IOException ex) {
            Logger.getLogger(OdtFiller.class.getName()).log(Level.SEVERE, "error", ex);
        }
        return hex;
    }
    
    protected Map<String, String> getFormFieldValues() {
        Map<String, String> map = new HashMap<String, String>();
        for(OdtField f: fields) {
            String n = f.getName();
            String v = f.getEditor().getStorableStringValue();
            map.put(n, v);
        }
        return map;
    }
    
    public String generateMeta(String sourceMetaXml) {
        String data = getFormFieldValueHexBuffer();
        XmlDoc doc = XmlDoc.newInstanceFromXml(sourceMetaXml);
        XmlDoc.XmlNode parent = doc.getRoot().getFirstChild("office:meta");
        XmlDoc.XmlNode child = parent.createChild("meta:user-defined");
        child.setAttribute("meta:name", "formdata");
        child.setTextContent(data);
        sourceMetaXml = doc.toXmlString();
        return sourceMetaXml;
    }
    
    public  void acquireDataFromOdtDocument(FileExt file) {
        System.out.println("acquiring...");
        Zipper zipper = new Zipper(file);
        String meta = zipper.getEntryAsText("meta.xml", "utf-8");
        XmlDoc doc = XmlDoc.newInstanceFromXml(meta);
        XmlDoc.XmlNode dataHolder = null;
        XmlDoc.XmlNode parent = doc.getRoot().getFirstChild("office:meta");
        for (XmlDoc.XmlNode n : parent.listChildren("meta:user-defined")) {
            if(n.hasAttributeValue("meta:name", "formdata")) {
                dataHolder= n;
                break;
            }
        }
        if(dataHolder != null && dataHolder.hasAttributeValue("meta:name", "formdata")) {
            String hex = dataHolder.getContentAsString();
            MemBuffer buffer = MemBuffer.newInstanceFromHexBytes(hex);
            acquireDataFromSerializedMap(buffer.getInputStream());
        } else {
            Logger.getLogger(OdtFiller.class.getName()).log(Level.INFO, "no formdata in document " + file);
        }
    }
    
    protected void acquireDataFromSerializedMap(InputStream in) {
        try {
            ObjectInputStream oin = new ObjectInputStream(in);
            Map<?, ?> map = Map.class.cast(oin.readObject());
            oin.close();
            acquireDataFromMap(map);
        } catch (Exception ex) {
            Logger.getLogger(OdtFiller.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    private final List<Function1<Map<String, StringExt>, ?>> postDroppers = new LinkedList<Function1<Map<String, StringExt>, ?>>();
    
    public void addPostDropTransform(Function1<Map<String, StringExt>, ?> fun) { postDroppers.add(fun); }
    
    protected void acquireDataFromMap(Map<?, ?> map) {
        Map<String, StringExt> values = new HashMap<String, StringExt>();
        for (Map.Entry<? extends Object, ? extends Object> entry : map.entrySet()) {
            String fieldName = String.valueOf(entry.getKey());
            StringExt fieldValue = StringExt.wrap(entry.getValue());
            
            values.put(fieldName, fieldValue);
            
            OdtField fa = getField(fieldName);
            OdtField fb = getField(fieldName.toLowerCase());
            OdtField fc = getField(fieldName.toUpperCase());
            OdtField field = fa;
            if(field == null) field = fb;
            if(field == null) field = fc;
            if(field != null) {
                field.getEditor().setValue(fieldValue);
            } else {
                System.out.println("No field named: " + fieldName);
            }
        }
        for (Function1<Map<String, StringExt>, ?> function : postDroppers) {
            function.apply(values);
        }
    }

    private OdtField getField(String fieldName) {
        for (OdtField odtField : fields) {
            if(odtField.getName().equals(fieldName)) return odtField;
        }
        return null;
    }
}
